import importlib
import conf.settings as settings
from lib.metric_exception import MetricSettingsException
from lib.metric_type.capacity import CapacityMetric
from lib.metric_type.load import LoadMetric
from lib.metric_type.latency import LatencyMetric
from lib.metric_type.error import ErrorMetric
from lib.metric_type.metric_type import Level
from metric_reader import dispatch_metric_reader

CUSTOM_METRIC_DIR = "custom_metric"
PROMETHEUS_CONFIG = settings.PROMETHEUS_CONFIG

METRIC_TYPE = {
    "CapacityMetric": CapacityMetric,
    "LoadMetric": LoadMetric,
    "LatencyMetric": LatencyMetric,
    "ErrorMetric": ErrorMetric
}

WEIGHT_METHODS = {
    Level.Cluster: settings.CLUSTER_WEIGHT_METHOD,
    Level.Node: settings.NODE_WEIGHT_METHOD,
    Level.Pod: settings.POD_WEIGHT_METHOD
}


class MetricManager():
    def __init__(self):
        self.registed_metric = {}
        self.interval = settings.CALCULATE_INTERVAL
        self.metric_reader = dispatch_metric_reader(
            "prometheus://" + PROMETHEUS_CONFIG.host + ":" + str(PROMETHEUS_CONFIG.port))

    def _metric_register(self, all_metrics, level):
        def __check_metric_weight(metric_list):
            """
            Make sure sum of same type of metric equal to 1
            """
            if len(metric_list) <= 0:
                return

            sum_weight = sum(
                item.settings.score.weight for item in metric_list
            )

            if sum_weight != 1:
                raise MetricSettingsException(
                    f"weight of {metric_type} level {level} setting error!"
                )

        self.registed_metric[level] = {}

        for metric_type, metrics in all_metrics.items():
            self.registed_metric[level][metric_type] = []

            for metric in metrics:
                if "filename" in metric["Collect"] \
                        and metric["Collect"]["filename"] != "":
                    # load non_standard metric from file
                    filename = metric["Collect"]["filename"]
                    module_name = f'{CUSTOM_METRIC_DIR}.{filename}'
                    class_name = filename.title().replace("_", "")
                    try:
                        metric_module = importlib.import_module(module_name)
                        if hasattr(metric_module, class_name):
                            metric_class = getattr(metric_module, class_name)
                            metric_instance = metric_class(self.metric_reader,
                                                           metric, level)
                    except ModuleNotFoundError as exc:
                        raise MetricSettingsException(
                            f"{module_name} not exist!"
                        ) from exc
                    except MetricSettingsException as exc:
                        raise exc

                else:
                    metric_class = METRIC_TYPE.get(metric_type)
                    if metric_class is not None:
                        metric_instance = metric_class(
                            self.metric_reader, metric, level)

                self.registed_metric[level][metric_type].append(
                    metric_instance)

            if WEIGHT_METHODS.get(level) == "WeightedSum":
                __check_metric_weight(self.registed_metric[level][metric_type])

    def metric_register(self):
        """
        Register all metrics to metric manager from settings
        """
        try:
            self._metric_register(settings.CLUSTER_METRICS, Level.Cluster)
            self._metric_register(settings.POD_METRICS, Level.Pod)
            self._metric_register(settings.NODE_METRICS, Level.Node)
        except MetricSettingsException as exc:
            raise exc
